#include "bsp_sdmmc.h"
#include "bsp.h"
#if (CONFIG_OS_ENABLE == 1)
#include "os_sal.h"
#endif

/* BSP-SDMMMC2 */

/**
 * SDMMC2不能被ITCM和DTCM内存访问(0x00000000-0x0000FFFF, 0x20000000-0x2001FFFF)
 * 使用时注意buf内存地址
 * 推荐使用AXI-SRAM(0x24000000-0x2407FFFF 512K)
 * SRAM1,SRAM2,SRAM3亦可
 * 
 */

/**
 * SDMMC2_CMD : PD7 - AF11
 * SDMMC2_SCK : PD6 - AF11
 * SDMMC2_IO0 : PB14 - AF9
 * SDMMC2_IO1 : PB15 - AF9
 * SDMMC2_IO2 : PB3 - AF9
 * SDMMC2_IO3 : PB4 - AF9
 * 
 */

#define SD2_DET_PORT    GPIOE
#define SD2_DET_PIN     GPIO_PIN_15

/* define */
#define SD2_TIMEOUT     5000UL

#define SD2_DMA_CACHE_MAINTENANCE_WRITE_ENABLE  0    /* bsp.c文件已经将AXI SRAM设置为WT模式，写操作无需再手动操作Cache实现Clean */
#define SD2_DMA_CACHE_MAINTENANCE_READ_ENABLE   1

/* static var */

static void (*bsp_sd1_irq_handler)(void) = NULL;
static void (*bsp_sd2_irq_handler)(void) = NULL;

static uint8_t flag_init = 0;
static SD_HandleTypeDef sd2_handle;
#if (CONFIG_OS_ENABLE == 1)
static os_mutex_handle_t sd2_mutex = NULL;
static os_sem_handle_t sd2_read_dma_sem = NULL;
static os_sem_handle_t sd2_write_dma_sem = NULL;
#else
static uint8_t sd2_read_dma_sta = 0;
static uint8_t sd2_write_dma_sta = 0;
#endif
static uint8_t ReadStatus = 0;

// 32-Byte aligned for cache maintenance
ALIGN(32) static uint8_t sd2_write_buff[BLOCKSIZE];
ALIGN(32) static uint8_t sd2_read_buff[BLOCKSIZE];


/* IRQ func */
void SDMMC1_IRQHandler(void)
{
    if (bsp_sd1_irq_handler)
    {
        bsp_sd1_irq_handler();
    }
}

void SDMMC2_IRQHandler(void)
{
    if (bsp_sd2_irq_handler)
    {
        bsp_sd2_irq_handler();
    }
    HAL_SD_IRQHandler(&sd2_handle);
}

/**
  * @brief Rx Transfer completed callbacks
  * @param hsd: SD handle
  * @retval None
  */
void HAL_SD_RxCpltCallback(SD_HandleTypeDef *hsd)
{
    if (hsd->Instance == SDMMC2)
    {
    #if (CONFIG_OS_ENABLE == 1)
        os_sem_release(sd2_read_dma_sem);
    #else
        sd2_read_dma_sta = 1;
    #endif
        ReadStatus = 1;
    }
}

/**
  * @brief Tx Transfer completed callbacks
  * @param hsd: SD handle
  * @retval None
  */
void HAL_SD_TxCpltCallback(SD_HandleTypeDef *hsd)
{
    if (hsd->Instance == SDMMC2)
    {
    #if (CONFIG_OS_ENABLE == 1)
        os_sem_release(sd2_write_dma_sem);
    #else
        sd2_write_dma_sta = 1;
    #endif
    }
}


/* static func */

static __INLINE uint8_t bsp_sdmmc2_read_dma_wait_sync()
{
    uint8_t sta;
    uint32_t timeout;

#if (CONFIG_OS_ENABLE == 1)
    {
        os_status_t os_sta;
        os_sta = os_sem_take(sd2_read_dma_sem, SD2_TIMEOUT);
        if (os_sta != OS_STA_SUCCESS) 
        {
            return HAL_ERROR;
        }
    }
#else
    {
        timeout = HAL_GetTick();
        while((sd2_read_dma_sta == 0) && ((HAL_GetTick() - timeout) < SD2_TIMEOUT))
        {
        }
        /* incase of a timeout return error */
        if (sd2_read_dma_sta == 0)
        {
            return HAL_TIMEOUT;
        }
        sd2_read_dma_sta = 0;
    }
#endif
    sta = 1;
    timeout = HAL_GetTick();
    while((HAL_GetTick() - timeout) < SD2_TIMEOUT)
    {
        if (HAL_SD_GetCardState(&sd2_handle) == HAL_SD_CARD_TRANSFER)
        {
            sta = 0;
        #if (SD2_DMA_CACHE_MAINTENANCE_READ_ENABLE == 1)
            SCB_CleanInvalidateDCache();
        #endif
            break;
        }
    }

    return sta;
}

static __INLINE uint8_t bsp_sdmmc2_write_dma_wait_sync()
{
    uint32_t timeout;

#if (CONFIG_OS_ENABLE == 1)
    {
        os_status_t os_sta;
        os_sta = os_sem_take(sd2_write_dma_sem, SD2_TIMEOUT);
        if (os_sta != OS_STA_SUCCESS) 
        {
            return HAL_ERROR;
        }
    }
#else
    {
        
        timeout = HAL_GetTick();
        while((sd2_write_dma_sta == 0) && ((HAL_GetTick() - timeout) < SD2_TIMEOUT))
        {
        }
        /* incase of a timeout return error */
        if (sd2_write_dma_sta == 0)
        {
            return HAL_TIMEOUT;
        }
    }
#endif
    timeout = HAL_GetTick();
    while((HAL_GetTick() - timeout) < SD2_TIMEOUT)
    {
        if (HAL_SD_GetCardState(&sd2_handle) == HAL_SD_CARD_TRANSFER)
        {
            break;
        }
    }

    return HAL_OK;
}

/* export func */


/**
 * @brief 初始化
 * 
 */
void bsp_sdmmc2_init(void)
{
    GPIO_InitTypeDef GPIO_InitStruct;

    if (flag_init) return ;

    __HAL_RCC_GPIOB_CLK_ENABLE();
    __HAL_RCC_GPIOD_CLK_ENABLE();

    /* SDMMC2_CMD : PD7 - AF11 */
    /* SDMMC2_SCK : PD6 - AF11 */
    GPIO_InitStruct.Pin = GPIO_PIN_6 | GPIO_PIN_7;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF11_SDMMC2;
    HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);

    /* SDMMC2_IO0 : PB14 - AF9 */
    /* SDMMC2_IO1 : PB15 - AF9 */
    /* SDMMC2_IO2 : PB3 - AF9 */
    /* SDMMC2_IO3 : PB4 - AF9 */
    GPIO_InitStruct.Pin = GPIO_PIN_3 | GPIO_PIN_4 | GPIO_PIN_14 | GPIO_PIN_15;
    GPIO_InitStruct.Alternate = GPIO_AF9_SDMMC2;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

#if 1
    /* SD detect io : PE15 */
    __HAL_RCC_GPIOE_CLK_ENABLE();
    GPIO_InitStruct.Pin = SD2_DET_PIN;
    GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(SD2_DET_PORT, &GPIO_InitStruct);
#endif

    /* sdmmc2 init */
    __HAL_RCC_SDMMC2_CLK_ENABLE();
    __HAL_RCC_SDMMC2_FORCE_RESET();
    __HAL_RCC_SDMMC2_RELEASE_RESET();

    sd2_handle.Instance = SDMMC2;
    // sd2_handle.Init.ClockDiv = SDMMC_NSpeed_CLK_DIV;
    sd2_handle.Init.ClockDiv = SDMMC_HSpeed_CLK_DIV;
    sd2_handle.Init.ClockPowerSave = SDMMC_CLOCK_POWER_SAVE_DISABLE;
    sd2_handle.Init.ClockEdge = SDMMC_CLOCK_EDGE_RISING;
    sd2_handle.Init.HardwareFlowControl = SDMMC_HARDWARE_FLOW_CONTROL_DISABLE;
    sd2_handle.Init.BusWide = SDMMC_BUS_WIDE_4B;
    HAL_SD_Init(&sd2_handle);

    /* SDMMC2 IRQ */
    HAL_NVIC_SetPriority(SDMMC2_IRQn, 9, 0);
    HAL_NVIC_EnableIRQ(SDMMC2_IRQn);

    

#if (CONFIG_OS_ENABLE == 1)
    sd2_mutex = os_mutex_create();
    sd2_read_dma_sem = os_sem_binary_create();
    sd2_write_dma_sem = os_sem_binary_create();
#endif

    flag_init = 1;
}

/**
 * @brief 去初始化
 * 
 */
void bsp_sdmmc2_deinit(void)
{
    sd2_handle.Instance = SDMMC2;

    HAL_SD_DeInit(&sd2_handle);

    HAL_NVIC_DisableIRQ(SDMMC2_IRQn);

    __HAL_RCC_SDMMC2_CLK_DISABLE();

    flag_init = 0;
}

/**
 * @brief 获取sdmmc2句柄
 * 
 * @return SD_HandleTypeDef* 
 */
SD_HandleTypeDef *bsp_sdmmc2_get_handle(void)
{
    return &sd2_handle;
}


void bsp_sdmmc2_lock(void)
{
#if (CONFIG_OS_ENABLE == 1)
    os_mutex_take(sd2_mutex, OS_WAIT_MAX);
#endif
}

void bsp_sdmmc2_unlock(void)
{
#if (CONFIG_OS_ENABLE == 1)
    os_mutex_release(sd2_mutex);
#endif
}

/**
 * @brief sdmmc2 擦除
 * 
 * @param addr_start 
 * @param addr_end 
 * @return uint8_t 
 */
uint8_t bsp_sdmmc2_erase(uint32_t addr_start, uint32_t addr_end)
{
    uint8_t sta;

    sta = HAL_SD_Erase(&sd2_handle, addr_start, addr_end);
    return sta;
}

/**
 * @brief sdmmc2 读取
 * 
 * @param addr 地址
 * @param data 读取缓存
 * @param size Number of SD blocks to read
 * @return uint8_t 
 */
uint8_t bsp_sdmmc2_read(uint32_t addr, uint8_t *data, uint32_t size, uint32_t timeout)
{
    return HAL_SD_ReadBlocks(&sd2_handle, data, addr, size, timeout);
}

/**
 * @brief sdmmc2 写入
 * 
 * @param addr 地址
 * @param data 写入缓存
 * @param size Number of SD blocks to write
 * @return uint8_t 
 */
uint8_t bsp_sdmmc2_write(uint32_t addr, uint8_t *data, uint32_t size, uint32_t timeout)
{
    return HAL_SD_WriteBlocks(&sd2_handle, data, addr, size, timeout);
}

/**
 * @brief sdmmc2块读取 - dma
 * 
 * @param addr 
 * @param data 
 * @param blocks 
 * @return uint8_t 
 */
uint8_t bsp_sdmmc2_read_blocks_dma(uint32_t addr, uint32_t *data, uint32_t blocks)
{
#if 1
    HAL_StatusTypeDef sta = HAL_OK;
    uint8_t ret;

    if (!((uint32_t)data & 0x3)) // 4字节对齐
    {
        sta = HAL_SD_ReadBlocks_DMA(&sd2_handle, (uint8_t *)data, addr, blocks);
        if (sta != HAL_OK) return sta;

        bsp_sdmmc2_read_dma_wait_sync();
    }
    else
    {
        int i;

        for (i = 0; i < blocks; i++)
        {
            sta = HAL_SD_ReadBlocks_DMA(&sd2_handle, (uint8_t*)sd2_read_buff, (uint32_t)addr++, 1);
            if (sta != HAL_OK) break;
            
            ret = bsp_sdmmc2_read_dma_wait_sync();
            if (ret) { sta = HAL_ERROR; break; }

            memcpy(data, sd2_read_buff, BLOCKSIZE);
            data += BLOCKSIZE;
            
        }
        if ((i == blocks) && (sta == HAL_OK)) sta = HAL_OK;
        else sta = HAL_ERROR;
    }
    
    return (uint8_t)sta;
#else
    uint8_t res = 1;
    uint32_t timeout;
    ReadStatus = 0;

    if (!((uint32_t)data & 0x3))
    {
        if(HAL_SD_ReadBlocks_DMA(&sd2_handle, (uint8_t*)data, (uint32_t)(addr), blocks) == 0)
        {
            /* Wait that the reading process is completed or a timeout occurs */
            timeout = HAL_GetTick();
            while((ReadStatus == 0) && ((HAL_GetTick() - timeout) < SD2_TIMEOUT))
            {
            }
            
            /* incase of a timeout return error */
            if (ReadStatus == 0)
            {
                res = 1;
                printf("sd read timout1\r\n");
            }
            else
            {
                ReadStatus = 0;
                timeout = HAL_GetTick();

                while((HAL_GetTick() - timeout) < SD2_TIMEOUT)
                {
                    if (HAL_SD_GetCardState(&sd2_handle) == HAL_SD_CARD_TRANSFER)
                    // if (BSP_SD_GetCardState() == SD_TRANSFER_OK)
                    {
                        res = 0;
                    #if (SD2_DMA_CACHE_MAINTENANCE_READ_ENABLE == 1)
                        SCB_CleanInvalidateDCache();
                    #endif
                        break;
                    }
                }
            }
        }
    }
    else
    {
        uint8_t ret = 1;
        int i;

        for (i = 0; i < blocks; i++) 
        {
            ret = HAL_SD_ReadBlocks_DMA(&sd2_handle, (uint8_t*)sd2_read_buff, (uint32_t)addr++, 1);
            if(ret == 0)
            {
                /* Wait that the reading process is completed or a timeout occurs */
                timeout = HAL_GetTick();
                while((ReadStatus == 0) && ((HAL_GetTick() - timeout) < SD2_TIMEOUT))
                {
                    
                }
                /* incase of a timeout return error */
                if (ReadStatus == 0)
                {
                    printf("sd read timout2\r\n");
                    break;
                }
                else
                {
                    ReadStatus = 0;
                    timeout = HAL_GetTick();

                    while((HAL_GetTick() - timeout) < SD2_TIMEOUT)
                    {
                        if (HAL_SD_GetCardState(&sd2_handle) == HAL_SD_CARD_TRANSFER)
                        // if (BSP_SD_GetCardState() == SD_TRANSFER_OK)
                        {
                            #if (ENABLE_SD_DMA_CACHE_MAINTENANCE_READ == 1)
                                SCB_CleanInvalidateDCache();
                            #endif
                            
                            memcpy(data, sd2_read_buff, BLOCKSIZE);
                            data += BLOCKSIZE;
                            
                            break;
                        }
                    }
                }
            }
            else
            {
                break;
            }
        }
        if ((i == blocks) && (ret == 0))
        {
           res = 0;       
        }
    }
    return res;
#endif
}

/**
 * @brief sdmmc2块写入 - dma
 * 
 * @param addr 
 * @param data 
 * @param blocks 
 * @return uint8_t 
 */
uint8_t bsp_sdmmc2_write_blocks_dma(uint32_t addr, uint32_t *data,  uint32_t blocks)
{
    HAL_StatusTypeDef sta = HAL_OK;

#if (SD2_DMA_CACHE_MAINTENANCE_WRITE_ENABLE == 1)
    SCB_CleanInvalidateDCache();
#endif

    if (!((uint32_t)data & 0x3)) // 4字节对齐
    {
        printf("sdmmc2 write 4bytes \r\n");
        sta = HAL_SD_WriteBlocks_DMA(&sd2_handle, (uint8_t *)data, addr, blocks);
        if (sta != HAL_OK) return sta;
        bsp_sdmmc2_write_dma_wait_sync();
    }
    else
    {
        int i;

        for (i = 0; i < blocks; i++)
        {
        #if (CONFIG_OS_ENABLE == 1)
        #else
            sd2_write_dma_sta = 0;
        #endif
            memcpy((void*)sd2_write_buff, data, BLOCKSIZE);
            data += BLOCKSIZE;

            sta = HAL_SD_WriteBlocks_DMA(&sd2_handle, (uint8_t *)sd2_write_buff, addr+i, 1);
            if (sta != HAL_OK) break;

            bsp_sdmmc2_write_dma_wait_sync();
        }

        if ((i == blocks) && (sta == HAL_OK)) sta = HAL_OK;         
        else sta = HAL_ERROR;
    }

    return (uint8_t)sta;
}




uint32_t bsp_sdmmc2_get_card_state(void)
{
    return HAL_SD_GetCardState(&sd2_handle);
}



uint32_t bsp_sdmmc2_get_card_type(void)
{
    return sd2_handle.SdCard.CardType;
}

uint32_t bsp_sdmmc2_get_card_version(void)
{
    return sd2_handle.SdCard.CardVersion;
}

uint32_t bsp_sdmmc2_get_card_speed(void)
{
    return sd2_handle.SdCard.CardSpeed;
}

uint32_t bsp_sdmmc2_get_card_addr(void)
{
    return sd2_handle.SdCard.RelCardAdd;
}

uint32_t bsp_sdmmc2_get_class(void)
{
    return sd2_handle.SdCard.Class;
}

uint32_t bsp_sdmmc2_get_block_nbr(void)
{
    return sd2_handle.SdCard.BlockNbr;
}

uint32_t bsp_sdmmc2_get_block_size(void)
{
    return sd2_handle.SdCard.BlockSize;
}

uint32_t bsp_sdmmc2_get_block_logical_nbr(void)
{
    return sd2_handle.SdCard.LogBlockNbr;
}

uint32_t bsp_sdmmc2_get_block_logical_size(void)
{
    return sd2_handle.SdCard.LogBlockSize;
}



/**
 * @brief 制造商ID
 * 
 * @return uint32_t 
 */
uint8_t bsp_sdmmc2_get_manufacturer_id(void)
{
    return (uint8_t)((sd2_handle.CID[0] & 0xFF000000U) >> 24U);
}

uint16_t bsp_sdmmc2_get_oem_appli_id(void)
{
    return (uint16_t)((sd2_handle.CID[0] & 0x00FFFF00U) >> 8U);
}

uint32_t bsp_sdmmc2_get_prod_name_1(void)
{
    return (((sd2_handle.CID[0] & 0x000000FFU) << 24U) | ((sd2_handle.CID[1] & 0xFFFFFF00U) >> 8U));
}

uint8_t bsp_sdmmc2_get_prod_name_2(void)
{
    return (uint8_t)(sd2_handle.CID[1] & 0x000000FFU);
}

/**
 * @brief 产品修订版本
 * 
 * @return uint8_t 
 */
uint8_t bsp_sdmmc2_get_prod_rev(void)
{
    return (uint8_t)((sd2_handle.CID[2] & 0xFF000000U) >> 24U);
}

/**
 * @brief 产品序列号
 * 
 * @return uint32_t 
 */
uint32_t bsp_sdmmc2_get_prod_sn(void)
{
    return (((sd2_handle.CID[2] & 0x00FFFFFFU) << 8U) | ((sd2_handle.CID[3] & 0xFF000000U) >> 24U));

}

/**
 * @brief 制造日期
 * 
 * @return uint16_t 
 */
uint16_t bsp_sdmmc2_get_manufact_date(void)
{
    return (uint16_t)((sd2_handle.CID[3] & 0x000FFF00U) >> 8U);
}





/**
 * @brief SD卡插入检测
 * 
 * @return uint8_t 1,已插入  0,未插入
 */
uint8_t bsp_sdmmc2_is_exist(void)
{
    if (bsp_gpio_pin_get(SD2_DET_PORT, SD2_DET_PIN) == 0) return 1;
    return 0;
}

